package com.lan.parse;

import com.lan.ScriptContext;
import com.lan.antlr.ScriptBaseListener;
import com.lan.antlr.ScriptParser;
import com.lan.model.Tv;
import com.lan.model.Type;
import com.lan.model.Variable;
import com.lan.model.symbol.*;
import com.lan.util.ImportContext;
import com.lan.util.ParseUtils;
import org.antlr.v4.runtime.ParserRuleContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

public class ParseJslListener extends ScriptBaseListener {

    private ScriptContext context;

    private Stack<Scope> scopeStack = new Stack<>();

    public ParseJslListener(ScriptContext context) {
        this.context = context;
    }

    private void print(ParserRuleContext ctx, String name) {
        System.out.println(ctx.getRuleIndex() + " :   " + ctx.toString() + "   " + (StringUtils.isNotBlank(name) ? name : "") + " " + (StringUtils.isNotBlank(ctx.getText()) ? ctx.getText() : ""));
    }

    private Scope pushScope(Scope scope, ParserRuleContext ctx) {
        context.nodeOfScope(ctx, scope);
        scope.setCtx(ctx);
        scopeStack.push(scope);
        return scope;
    }

    /**
     * 从栈中弹出scope
     */
    private void popScope() {
        scopeStack.pop();
    }

    /**
     * 在遍历树的过程中，当前的Scope
     *
     * @return
     */
    private Scope scope() {
        if (scopeStack.size() > 0) {
            return scopeStack.peek();
        } else {
            return null;
        }
    }

    private String getName() {
        Scope scope = scope();
        if (scope != null) {
            return scope.getId();
        }
        return "";
    }

    @Override
    public void enterProg(ScriptParser.ProgContext ctx) {
        Ns scope = new Ns("ns", "ns", scope(), ctx);
        context.setNs(scope);
        pushScope(scope, ctx);
        print(ctx, "enterProg");
    }

    @Override
    public void exitProg(ScriptParser.ProgContext ctx) {
        popScope();
        print(ctx, "exitProg");
//        List<Symbol> list = context.getNs().getSymbols();
    }

    @Override
    public void enterFunction(ScriptParser.FunctionContext ctx) {
        if (!(ctx.parent instanceof ScriptParser.ExpressionContext)) {
            return;
        }
        String name = getName();
        Function function = new Function(name, ctx.getText(), scope(), ctx);
        pushScope(function, ctx);
    }

    @Override
    public void exitFunction(ScriptParser.FunctionContext ctx) {
        if (!(ctx.parent instanceof ScriptParser.ExpressionContext)) {
            return;
        }
        popScope();
    }


    @Override
    public void enterImportStatement(ScriptParser.ImportStatementContext ctx) {
        List<ScriptParser.NameContext> nameContextList = ctx.name();
        if (CollectionUtils.isNotEmpty(nameContextList)) {
            List<String> nameList = new ArrayList<>();
            for (int i = 0; i < nameContextList.size(); i++) {
                ScriptParser.NameContext nameContext = nameContextList.get(i);
                nameList.add(nameContext.getText());
            }
            ImportContext importContext = context.getImportContext();
            importContext.put(nameList.get(nameList.size() - 1), StringUtils.join(nameList, "."));
        }
    }

    @Override
    public void enterParameters(ScriptParser.ParametersContext ctx) {
        List<ScriptParser.NameContext> list = ctx.name();
        if (CollectionUtils.isNotEmpty(list)) {
            Function scope = (Function) scope();
            String name = getName();
            for (int i = 0; i < list.size(); i++) {
                ScriptParser.NameContext o = list.get(i);
                Variable variable = new Variable(name, o.getText(), scope, o);
                scope.getParameters().add(variable);
            }
        }
    }

    @Override
    public void enterStatement(ScriptParser.StatementContext ctx) {
        //为for建立额外的Scope
        if (ctx.FOR() != null) {
            String name = getName();
            Block scope = new Block(name, "for", scope(), ctx);
            scope().add(scope);
            pushScope(scope, ctx);
            print(ctx, "enterStatement");
        }
    }

    @Override
    public void exitStatement(ScriptParser.StatementContext ctx) {
        //释放for语句的外层scope
        if (ctx.FOR() != null) {
            popScope();
            print(ctx, "exitStatement");
        }
    }

    @Override
    public void enterJson(ScriptParser.JsonContext ctx) {
        if (!(ctx.parent instanceof ScriptParser.ExpressionContext)) {
            return;
        }
        ScriptParser.ExpressionContext context = (ScriptParser.ExpressionContext) ctx.parent;
        Type type = null;
        Object data = null;
        if (ctx.obj() != null) {
            type = Type.JSON;
            data = new HashMap<String, Object>();
        } else if (ctx.arr() != null) {
            //第一次 数组 给一个范围 过后不给
            type = Type.JArray;
            data = new LinkedList<>();
        }

        if (context.propertysExp() != null) {
            //第一层 scope
            List<Tv> nameList = ParseUtils.getNameList(context.propertysExp());
            String varName = Tv.toString(nameList, "_");

            Json json = new Json(getName() + "_" + varName, varName, scope(), type, ctx);
            json.setData(data);
            scope().add(json);
            // 创建一个新的scope
            pushScope(json, ctx);
        } else {
            String varName = "return";
            Json json = new Json(getName() + "_" + varName, varName, scope(), type, ctx);
            json.setData(data);
            scope().add(json);
            pushScope(json, ctx);
        }
    }


    @Override
    public void enterObj(ScriptParser.ObjContext ctx) {
        List<ScriptParser.PairContext> pairContextList = ctx.pair();
        if (pairContextList != null && pairContextList.size() > 0) {
            Json scope = (Json) scope();
            Map<String, Object> dataMap = (Map<String, Object>) scope.getData();
            for (int i = 0; i < pairContextList.size(); i++) {
                ScriptParser.PairContext item = pairContextList.get(i);
                String name = item.key().getText();
                if (item.value().function() != null) {
                    Function function = new Function(getName() + "_" + name, name, scope(), item.value().function());
                    scope.add(function);
                    // 创建一个新的scope
                    pushScope(function, item.value().function());
                } else if (item.value().json() != null) {
                    if (item.value().json().obj() != null) {
                        Map<String, Object> data = new HashMap<>();
                        dataMap.put(name, data);

                        Json json = new Json(getName() + "_" + name, name, scope(), Type.JSON, ctx);

                        scope.add(json);
                        json.setData(data);
                        pushScope(json, item.value().json());

                    } else if (item.value().json().arr() != null) {
                        List<Object> data = new LinkedList<>();
                        dataMap.put(name, data);

                        Json json = new Json(getName() + "_" + name, name, scope(), Type.JArray, ctx);

                        scope.add(json);
                        json.setData(data);
                        pushScope(json, item.value().json().arr());
                    }
                } else if (item.value().literal() != null) {
                    dataMap.put(name, jsonPair(item.value().literal()));
                } else if (item.value().expression() != null) {
                    dataMap.put(name, null);
                    Variable variable = new Variable(Type.Expression_KEY, name);
                    variable.setCtx(item.value().expression());
                    scope.add(variable);
                }
            }
        }
    }


    private Object jsonPair(ScriptParser.LiteralContext literalContext) {
        Object rtn = null;
        if (literalContext.INTEGER_LITERAL() != null) {
            rtn = Integer.valueOf(literalContext.INTEGER_LITERAL().getText());
        } else if (literalContext.FLOAT_LITERAL() != null) {
            rtn = Float.valueOf(literalContext.FLOAT_LITERAL().getText());
        } else if (literalContext.BOOL_LITERAL() != null) {
            rtn = Boolean.valueOf(literalContext.BOOL_LITERAL().getText());
        } else if (literalContext.STRING_LITERAL() != null) {
            String str = literalContext.STRING_LITERAL().getText();
            rtn = str.substring(1, str.length() - 1);
        }
        return rtn;
    }


    @Override
    public void enterArr(ScriptParser.ArrContext ctx) {
        Json scope = (Json) scope();
        String name = getName();

        List<Object> dataList = (List<Object>) scope.getData();
        for (int i = 0; i < ctx.value().size(); i++) {
            ScriptParser.ValueContext valueContext = ctx.value(i);
            if (valueContext.function() != null) {
                Function function = new Function(name + "_array_" + i, i + "", scope(), valueContext.function());
                // 创建一个新的scope
                scope.add(function);
                pushScope(function, valueContext.function());
                dataList.add(null);
            } else if (valueContext.json() != null) {
                if (valueContext.json().obj() != null) {
                    Json json = new Json(getName() + "_array_" + i, i + "", scope(), Type.JSON, valueContext.json());
                    List<Object> data = new LinkedList<>();
                    dataList.add(data);
                    json.setData(data);
                    scope.add(json);
                    pushScope(json, valueContext.json());
                } else {
                    throw new RuntimeException("暂不支持二维数组");
                }
            } else if (valueContext.literal() != null) {
                dataList.add(jsonPair(valueContext.literal()));
            } else if (valueContext.expression() != null) {
                dataList.add(null);
                Variable variable = new Variable(Type.Expression_Array, i + "");
                variable.setCtx(valueContext.expression());
                scope.add(variable);
            }
        }
    }


    @Override
    public void exitJson(ScriptParser.JsonContext ctx) {
        popScope();
    }

    @Override
    public void exitObj(ScriptParser.ObjContext ctx) {
        List<ScriptParser.PairContext> pairContextList = ctx.pair();
        if (pairContextList != null && pairContextList.size() > 0) {
            for (int i = 0; i < pairContextList.size(); i++) {
                ScriptParser.PairContext item = pairContextList.get(i);
                if (item.value().function() != null) {
                    popScope();
                }
            }
        }
    }

    @Override
    public void exitArr(ScriptParser.ArrContext ctx) {
        for (int i = 0; i < ctx.value().size(); i++) {
            ScriptParser.ValueContext valueContext = ctx.value(i);
            if (valueContext.function() != null) {
                popScope();
            }
        }
    }
}
